// VINST.h
// This file contains the definition of intermediate representation
#ifndef VINST_H
#define VINST_H

#include "crossbit/common.h"
#include <iostream>
#include <vector>

namespace crossbit{
#include <assert.h>
#include <stdarg.h>
}

namespace crossbit {

	// -------------------------------------------------------------------
	//	    Shortcuts for creating virtual instructions
	// -------------------------------------------------------------------

#define PUT(a, b)		(VInst2::createPUT((a), (b)))
#define GET(a, b)		(VInst2::createGET((a), (b)))

#define LD(a, b, c, d)		(VInst4::createLD((a), (b), (c), (d)))
#define ST(a, b, c, d)		(VInst4::createST((a), (b), (c), (d)))

#define MOV(a, b)		(VInst2::createMOV((a), (b)))
#define LI(a, b)		(VInst2::createLI((a), (b)))

#define SEXT(a, b, c)		(VInst3::createSEXT((a), (b), (c)))
#define ZEXT(a, b, c)		(VInst3::createZEXT((a), (b), (c)))
#define ADD(a, b, c)		(VInst3::createADD((a), (b), (c)))
#define SUB(a, b, c)		(VInst3::createSUB((a), (b), (c)))
#define AND(a, b, c)		(VInst3::createAND((a), (b), (c)))
#define OR(a, b, c)		(VInst3::createOR((a), (b), (c)))
#define XOR(a, b, c)		(VInst3::createXOR((a), (b), (c)))
#define NOT(a, b)		(VInst2::createNOT((a), (b)))
#define MUL(a, b, c, d)		(VInst4::createMUL((a), (b), (c), (d)))
#define MULU(a, b, c, d)	(VInst4::createMULU((a), (b), (c), (d)))
#define DIV(a, b, c, d)		(VInst4::createDIV((a), (b), (c), (d)))
#define DIVU(a, b, c, d)	(VInst4::createDIVU((a), (b), (c), (d)))
#define SLL(a, b, c)		(VInst3::createSLL((a), (b), (c)))
#define SRL(a, b, c)		(VInst3::createSRL((a), (b), (c)))
#define SRA(a, b, c)		(VInst3::createSRA((a), (b), (c)))
	//#define SLLV(a, b, c)		(VInst3::createSLLV((a), (b), (c)))
	//#define SRLV(a, b, c)		(VInst3::createSRLV((a), (b), (c)))
	//#define SRAV(a, b, c)		(VInst3::createSRAV((a), (b), (c)))
#define CMP(a, b, c, d)		(VInst4::createCMP((a), (b), (c), (d)))

#define JMP(a, b)		(VInst2::createJMP((a), (b)))
#define BRANCH(a, b, c, d, e)	(VInst5::createBRANCH((a), (b), (c), (d), (e)))

#define HALT(a)			(VInst0::createHALT(a))
#define SYSCALL			(VInst0::createSYSCALL())
#define CALL(a, b, ...)		(VInstVar::createCALL((a),(b),  __VA_ARGS__))

	// Float Point Instructions

#define FPUTS(a)		(VInst1::createFPUTS((a)))
#define FPUTD(a)		(VInst1::createFPUTD((a)))
#define FGETS(a)		(VInst1::createFGETS((a)))
#define FGETD(a)		(VInst1::createFGETD((a)))

#define FLD(a,b,c,d)		(VInst4::createFLD((a),(b),(c),(d)))
#define FST(a,b,c,d)		(VInst4::createFST((a),(b),(c),(d)))

#define FMOV(a,b)		(VInst2::createFMOV((a),(b)))

#define FADDS()			(VInst0::createFADDS())
#define FADDD()			(VInst0::createFADDD())
#define FSUBS()			(VInst0::createFSUBS())
#define FSUBD()			(VInst0::createFSUBD())
#define FMULS()			(VInst0::createFMULS())
#define FMULD()			(VInst0::createFMULD())
#define FDIVS()			(VInst0::createFDIVS())
#define FDIVD()			(VInst0::createFDIVD())
#define FNEGS()			(VInst0::createFNEGS())
#define FNEGD()			(VInst0::createFNEGD())
#define FABSS()			(VInst0::createFABSS())
#define FABSD()			(VInst0::createFABSD())
#define FSQRTS()		(VInst0::createFSQRTS())
#define FSQRTD()		(VInst0::createFSQRTD())
#define FCMPS(a,b,c,d)		(VInst4::createFCMPS((a),(b),(c),(d)))
#define FCMPD(a,b,c,d)		(VInst4::createFCMPD((a),(b),(c),(d)))

#define FCONV(a,b,c,d)		(VInst4::createFCONV((a),(b),(c),(d)))
#define FINCSTP()		(VInst0::createFINCSTP())
	// ---------------------------------------------------------------------



	// Compare Condition Code
	enum CmpCC {
		EQ,	// equal
		NE,	// not equal
		GT,	// greater than
		GE,	// greater than or equal
		LT,	// less than
		LE,	// less than or equal
		AB,	// above (unsigned GT)
		BE,	// below (unsigned LT)
		NA,	// not above
	};

	// VInstruction Operand
	typedef struct vinst_operand_t {
		enum operand_type {
			SREG,	// source register
			VREG_USE,   // virtual register that is used
			VREG_DEF,	// virtual register that is defined
			FREG,	// float-point register
			//MEM,
			//PC,
			IMM,
			SIZE,
			CC,
			WORD,
			INVALID,
		} type;

		union {
			// register number (including SREG, VREG_USE, VREG_DEF, FREG)
			XTRegNum reg;

			// immediate value
			XTInt32 imm;

			// BYTE / HALFWORD / WORD 
			BlockSize size;

			// compare condition
			CmpCC	cc;

			// just plain word
			XTInt32 word;
		};

		vinst_operand_t(enum operand_type t)
			: type(t)
			{
			}

		// Dummy anonymous constructor for initialize 
		// operand array
		vinst_operand_t()
			:type(INVALID)
			{
			}

	} Operand;

	enum Opcode {
		OP_GET,		OP_PUT,

		OP_LD,		OP_ST,

		OP_MOV,		OP_LI,

		OP_SEXT,	OP_ZEXT, 
		OP_ADD,		OP_SUB,	
		OP_AND,		OP_NOT, 
		OP_XOR,		OP_OR, 
		OP_MUL,		OP_MULU, 
		OP_DIV,		OP_DIVU, 
		OP_SLL,		OP_SRL,	    OP_SRA, 
		OP_SLLV,	OP_SRLV,    OP_SRAV,
		OP_CMP,

		OP_JMP,		OP_BRANCH,

		OP_HALT,	OP_SYSCALL,
		OP_CALL,
		//total 24 instructions

		//float point instructions
		OP_FPUT_S,	OP_FGET_S,
		OP_FPUT_D,	OP_FGET_D,

		OP_FLD,		OP_FST,

		OP_FMOV,

		OP_FADD_S,	OP_FADD_D,
		OP_FSUB_S,	OP_FSUB_D,
		OP_FMUL_S,	OP_FMUL_D,
		OP_FDIV_S,	OP_FDIV_D,
		OP_FNEG_S,	OP_FNEG_D,
		OP_FABS_S,	OP_FABS_D,
		OP_FSQRT_S,	OP_FSQRT_D,
		OP_FCMP_S,	OP_FCMP_D,

		OP_FCONV,	//total 24 float-point v-instructions
		OP_FINCSTP	//added by ZhengJuyu
	};	    

	enum Opkind {
		OPK_STATEMAP,		// map simulated machine state to/from virtual register
		OPK_MEMORYACCESS,	// load/store
		OPK_DATAMOVEMENT,	// register -> register, immediate -> register
		OPK_COMPUTATION,	// add, sub, and, not, xor, or, mul, div, shift, cmp ...
		OPK_CONTROLTRANSFER,	// jmp
		OPK_SPECIAL,		// any operation that doesn't fit into above categorie
	};

	// Virtual Instruction
	// base class of virtual instruction
	class VInst {
		// printing VInst for debugging purpose
		friend  std::ostream& operator<<(std::ostream&, const VInst&);

		public:
		virtual ~VInst() 
		{
		}

		inline Opcode getOpcode() const
		{
			return op;
		}

		inline Opkind getOpkind() const
		{
			return kind;
		}

		inline XTMemAddr getVAddr() const
		{
			return vaddr;
		}

		virtual Operand getOperand(int i) const = 0;
		virtual void setOperand(int i, Operand oprnd) = 0;
		virtual int getOperandNum() const = 0;

		protected:
		VInst (Opcode opcode, Opkind opkind)
			: op(opcode), kind(opkind)
			{}
		VInst (Opcode opcode, Opkind opkind, XTMemAddr addr)
			: op(opcode), kind(opkind), vaddr(addr)
			{}

		private:
		Opcode op;
		Opkind kind;

		//added by Zheng Juyu
		//The address of the source instruction
		XTMemAddr vaddr;
	};

	// VInst with NO operands
	// Including instructions:
	class VInst0 : public VInst {
		public:
			// Attention: undefined
			Operand getOperand(int i) const 
			{ 
				assert(0 && "No Operand Available"); 
				return Operand(); // dummy, shut off warnings
			}

			void setOperand(int i, Operand oprnd)
			{
				return;
			}

			int getOperandNum() const 
			{ 
				return 0; 
			}

			static VInst *createHALT(XTMemAddr addr) 
			{
				return new VInst0(OP_HALT, OPK_SPECIAL, addr);
			}

			static VInst *createSYSCALL()
			{ 
				return new VInst0(OP_SYSCALL, OPK_SPECIAL);
			}

			// float-point arithmetic v-instructions
			static VInst *createFADDS()
			{
				return new VInst0(OP_FADD_S, OPK_COMPUTATION);
			}
			static VInst *createFADDD()
			{
				return new VInst0(OP_FADD_D, OPK_COMPUTATION);
			}

			static VInst *createFSUBS()
			{
				return new VInst0(OP_FSUB_S, OPK_COMPUTATION);
			}

			static VInst *createFSUBD()
			{
				return new VInst0(OP_FSUB_D, OPK_COMPUTATION);
			}

			static VInst *createFMULS()
			{
				return new VInst0(OP_FMUL_S, OPK_COMPUTATION);
			}

			static VInst *createFMULD()
			{
				return new VInst0(OP_FMUL_D, OPK_COMPUTATION);
			}

			static VInst *createFDIVS()
			{
				return new VInst0(OP_FDIV_S, OPK_COMPUTATION);
			}

			static VInst *createFDIVD()
			{
				return new VInst0(OP_FDIV_D, OPK_COMPUTATION);
			}

			static VInst *createFNEGS()
			{
				return new VInst0(OP_FNEG_S, OPK_COMPUTATION);
			}

			static VInst *createFNEGD()
			{
				return new VInst0(OP_FNEG_D, OPK_COMPUTATION);
			}

			static VInst *createFABSS()
			{
				return new VInst0(OP_FABS_S, OPK_COMPUTATION);
			}

			static VInst *createFABSD()
			{
				return new VInst0(OP_FABS_D, OPK_COMPUTATION);
			}

			static VInst *createFSQRTS()
			{
				return new VInst0(OP_FSQRT_S, OPK_COMPUTATION);
			}

			static VInst *createFSQRTD()
			{
				return new VInst0(OP_FSQRT_D, OPK_COMPUTATION);
			}

			static VInst* createFINCSTP()
			{
				return new VInst0(OP_FINCSTP, OPK_COMPUTATION);
			} 

		protected:
			VInst0 (Opcode op, Opkind kind)
				: VInst (op, kind) {}
			VInst0 (Opcode op, Opkind kind, XTMemAddr addr)
				: VInst (op, kind, addr) {}
	};

	// VInst with ONE operand
	class VInst1 : public VInst {
		public:
			// Attention: only valide when i == 1, otherwise, undefined.
			Operand getOperand(int i) const
			{
				return operand;
			}

			void setOperand(int i, Operand oprnd)
			{
				if (i == 1)
					operand = oprnd;
			}

			int getOperandNum() const { return 1; }

			// r1: float point register number
			static VInst *createFGETS(XTRegNum fpr1)
			{
				Operand opr1(Operand::FREG);
				opr1.reg = fpr1;
				return new VInst1(OP_FGET_S, OPK_STATEMAP, opr1);
			}

			// r1: float point register number
			static VInst *createFPUTS(XTRegNum fpr1)
			{
				Operand opr1(Operand::FREG);
				opr1.reg = fpr1;
				return new VInst1(OP_FPUT_S, OPK_STATEMAP, opr1);
			}

			// r1: float point register number
			static VInst *createFGETD(XTRegNum fpr1)
			{
				Operand opr1(Operand::FREG);
				opr1.reg = fpr1;
				return new VInst1(OP_FGET_D, OPK_STATEMAP, opr1);
			}

			// r1: float point register number
			static VInst *createFPUTD(XTRegNum fpr1)
			{
				Operand opr1(Operand::FREG);
				opr1.reg = fpr1;
				return new VInst1(OP_FPUT_D, OPK_STATEMAP, opr1);
			}

		protected:
			VInst1 (Opcode op, Opkind kind, Operand opr) 
				: VInst(op, kind), operand(opr) {}

		private:
			Operand operand;
	};

	// VInst with TWO operands
	class VInst2 : public VInst {
		public:
			// Attention: only valid when i >= 1 && i <= 2, otherwise, undefined
			Operand getOperand(int i) const 
			{
				return operand[i - 1];
			}

			void setOperand(int i, Operand oprnd)
			{
				operand[i - 1] = oprnd;
			}

			int getOperandNum() const { return 2; }

			// r1: source s-register number
			// r2: destination v-register number
			static VInst * createGET(XTRegNum r1, XTRegNum r2)
			{
				Operand opr1(Operand::SREG), opr2(Operand::VREG_DEF);
				opr1.reg = r1;
				opr2.reg = r2;
				return new VInst2(OP_GET, OPK_STATEMAP, opr1, opr2);
			}

			// r1: source v-register number
			// r2: destination s-register number
			static VInst * createPUT(XTRegNum r1, XTRegNum r2)
			{
				Operand opr1(Operand::VREG_USE), opr2(Operand::SREG);
				opr1.reg = r1;
				opr2.reg = r2;
				return new VInst2(OP_PUT, OPK_STATEMAP, opr1, opr2);
			}


			// r1: source v-register number
			// r2: destination v-register number
			static VInst * createNOT (XTRegNum r1, XTRegNum r2)
			{
				Operand opr1(Operand::VREG_USE), opr2(Operand::VREG_DEF);
				opr1.reg = r1;
				opr2.reg = r2;
				return new VInst2(OP_NOT, OPK_COMPUTATION, opr1, opr2);
			}

			// r1: source v-register number
			// r2: destination v-register number
			static VInst * createMOV (XTRegNum r1, XTRegNum r2)
			{
				Operand opr1(Operand::VREG_USE), opr2(Operand::VREG_DEF);
				opr1.reg = r1;
				opr2.reg = r2;
				return new VInst2(OP_MOV, OPK_DATAMOVEMENT, opr1, opr2);
			}

			// r1: source float-point register number
			// r2: destination float-point register number
			static VInst * createFMOV (XTRegNum r1, XTRegNum r2)
			{
				Operand opr1(Operand::FREG), opr2(Operand::FREG);
				opr1.reg = r1;
				opr2.reg = r2;
				return new VInst2(OP_FMOV, OPK_DATAMOVEMENT, opr1, opr2);
			}

			// imm: source immediate value
			// reg: destination v-register number
			static VInst * createLI (XTInt32 imm, XTRegNum reg)
			{
				Operand opr1(Operand::IMM), opr2(Operand::VREG_DEF);
				opr1.imm = imm;
				opr2.reg = reg;
				return new VInst2(OP_LI, OPK_DATAMOVEMENT, opr1, opr2);
			}

			// (disp)reg: jump destination, where "type" specifies SPC or TPC.
			static VInst * createJMP(XTRegNum reg, XTInt32 disp)
			{
				Operand opr1(Operand::VREG_USE);
				Operand opr2(Operand::IMM);
				opr1.reg = reg;
				opr2.imm = disp;
				return new VInst2(OP_JMP, OPK_CONTROLTRANSFER, opr1, opr2);
			}
			/*
			   static VInst *createFLD(XTRegNum reg, XTInt32 disp)
			   {
			   Operand opr1(Operand::FREG),
			   opr2(Operand::IMM);

			   opr1.reg = reg;
			   opr2.imm = disp;
			   return new VInst2(OP_FLD, OPK_DATAMOVEMENT, opr1, opr2);
			   }

			   static VInst *createFST(XTRegNum reg, XTInt32 disp)
			   {
			   Operand opr1(Operand::FREG),
			   opr2(Operand::IMM);

			   opr1.reg = reg;
			   opr2.imm = disp;
			   return new VInst2(OP_FST, OPK_DATAMOVEMENT, opr1, opr2);
			   }
			 */
		protected:
			VInst2 (Opcode op, Opkind kind, Operand opr1, Operand opr2)
				: VInst(op, kind)
				{
					operand[0] = opr1;
					operand[1] = opr2;
				}

		private:
			Operand operand[2];
	};

	// VInst with THREE operands
	class VInst3 : public VInst {
		public:
			VInst3(Opcode op, Opkind kind, Operand opr1, Operand opr2, Operand opr3)
				: VInst(op, kind)
				{
					operand[0] = opr1;
					operand[1] = opr2;
					operand[2] = opr3;
				}

			// Attention: only valid when i >= 1 && i <= 3, otherwise, undefined
			Operand getOperand(int i) const
			{
				return operand[i - 1];
			}

			void setOperand(int i, Operand oprnd)
			{
				operand[i - 1] = oprnd;
			}

			int getOperandNum() const { return 3; }

			// r1: source v-register number
			// size: block size, specifies which part of "r1" will be extended
			// r2: destination v-register number
			static VInst * createSEXT (XTRegNum r1, BlockSize size, XTRegNum r2)	
			{
				Operand opr1(Operand::VREG_USE), 
					opr2(Operand::SIZE), 
					opr3(Operand::VREG_DEF);

				opr1.reg = r1;
				opr2.size = size;
				opr3.reg = r2;
				return new VInst3 (OP_SEXT, OPK_COMPUTATION, opr1, opr2, opr3);
			}

			// ditto
			static VInst * createZEXT (XTRegNum r1, BlockSize size, XTRegNum r2)
			{
				Operand opr1(Operand::VREG_USE), 
					opr2(Operand::SIZE), 
					opr3(Operand::VREG_DEF);

				opr1.reg = r1;
				opr2.size = size;
				opr3.reg = r2;
				return new VInst3 (OP_ZEXT, OPK_COMPUTATION, opr1, opr2, opr3);
			}

#define CREATECOMP3(op)  \
			static VInst *create##op (XTRegNum r1, XTRegNum r2, XTRegNum r3)	\
			{	\
				Operand opr1(Operand::VREG_USE),    \
				opr2(Operand::VREG_USE),    \
				opr3(Operand::VREG_DEF);    \
				opr1.reg = r1;		    \
				opr2.reg = r2;		    \
				opr3.reg = r3;		    \
				return new VInst3 (OP_##op, OPK_COMPUTATION, opr1, opr2, opr3);	\
			}

			CREATECOMP3(ADD)
				CREATECOMP3(SUB)
				CREATECOMP3(AND)
				CREATECOMP3(XOR)
				CREATECOMP3(OR)
#undef CREATECOMP3

				/*
#define CREATESHIFT(op)	\
static VInst *create##op (XTRegNum src, Byte n, XTRegNum dst)	\
{	\
Operand opr1(Operand::VREG_USE),    \
opr2(Operand::IMM),	    \
opr3(Operand::VREG_DEF);    \
opr1.reg = src;		    \
opr2.imm = n;		    \
opr3.reg = dst;		    \
return new VInst3 (OP_##op, OPK_COMPUTATION, opr1, opr2, opr3);	\
}	
CREATESHIFT(SRL)
CREATESHIFT(SRA)
CREATESHIFT(SLL)
#undef CREATESHIFT
				 */

#define CREATESHIFT(op)								\
		static VInst *create##op (XTRegNum src, XTRegNum shi, XTRegNum dst)	\
{									\
	Operand opr1(Operand::VREG_USE),				\
	opr2(Operand::VREG_USE),				\
	opr3(Operand::VREG_DEF);				\
	opr1.reg = src;							\
	opr2.reg = shi;							\
	opr3.reg = dst;							\
	return new VInst3 (OP_##op, OPK_COMPUTATION, opr1, opr2, opr3);	\
}	

	CREATESHIFT(SRL)
	CREATESHIFT(SRA)
CREATESHIFT(SLL)

#undef CREATESHIFT

	private:
	Operand operand[3];
	};

// VInst with FOUR operands
class VInst4 : public VInst {
	public:

		// Attention: only valid when i >= 1 && i <= 4, otherwise, undefined
		Operand getOperand(int i) const
		{
			return operand[i - 1];
		}

		void setOperand(int i, Operand oprnd)
		{
			operand[i - 1] = oprnd;
		}

		int getOperandNum() const { return 4; }

		// (disp)reg: memory address
		// size: block size
		// dst: destination v-register number
		static VInst * createLD(XTRegNum reg, XTInt32 disp, BlockSize size, XTRegNum dst)
		{
			Operand opr1(Operand::VREG_USE),
				opr2(Operand::IMM),
				opr3(Operand::SIZE), 
				opr4(Operand::VREG_DEF);

			opr1.reg = reg;
			opr2.imm = disp;
			opr3.size = size;
			opr4.reg = dst;
			return new VInst4(OP_LD, OPK_MEMORYACCESS, opr1, opr2, opr3, opr4);
		}


		// src: source v-register number
		// size: block size
		// (disp)reg: memory address
		static VInst * createST(XTRegNum src, BlockSize size, XTRegNum reg, XTInt32 disp)
		{
			Operand opr1(Operand::VREG_USE), 
				opr2(Operand::SIZE), 
				opr3(Operand::VREG_USE),
				opr4(Operand::IMM);

			opr1.reg = src;
			opr2.size = size;
			opr3.reg = reg;
			opr4.imm = disp;
			return new VInst4(OP_ST, OPK_MEMORYACCESS, opr1, opr2, opr3, opr4);
		}

		// (disp)reg: memory address
		// size: block size
		// dst: destination v-register number
		static VInst * createFLD(XTRegNum reg, XTInt32 disp, BlockSize size, XTRegNum dst)
		{
			Operand opr1(Operand::VREG_USE),
				opr2(Operand::IMM),
				opr3(Operand::SIZE), 
				opr4(Operand::FREG);

			opr1.reg = reg;
			opr2.imm = disp;
			opr3.size = size;
			opr4.reg = dst;
			return new VInst4(OP_FLD, OPK_MEMORYACCESS, opr1, opr2, opr3, opr4);
		}

		// src: source float-point register number
		// size: block size
		// (disp)reg: memory address
		static VInst * createFST(XTRegNum src, BlockSize size, XTRegNum reg, XTInt32 disp)
		{
			Operand opr1(Operand::FREG), 
				opr2(Operand::SIZE), 
				opr3(Operand::VREG_USE),
				opr4(Operand::IMM);

			opr1.reg = src;
			opr2.size = size;
			opr3.reg = reg;
			opr4.imm = disp;
			return new VInst4(OP_FST, OPK_MEMORYACCESS, opr1, opr2, opr3, opr4);
		}

		// src: source float-point register number
		// srcsize: source data size
		// dst: destination float-point register number
		// dstsize: destination data size
		static VInst * createFCONV(XTRegNum src, BlockSize srcsize, XTRegNum dst, BlockSize dstsize)
		{
			Operand opr1(Operand::FREG),
				opr2(Operand::SIZE),
				opr3(Operand::FREG), 
				opr4(Operand::SIZE);

			opr1.reg = src;
			opr2.size = srcsize;
			opr3.reg = dst;
			opr4.size = dstsize;
			return new VInst4(OP_FCONV, OPK_SPECIAL, opr1, opr2, opr3, opr4);
		}


#define CREATECOMP4(op)	\
		static VInst * create##op(XTRegNum r1, XTRegNum r2, XTRegNum r3, XTRegNum r4)	\
		{	    \
			Operand opr1(Operand::VREG_USE),    \
			opr2(Operand::VREG_USE),    \
			opr3(Operand::VREG_DEF),    \
			opr4(Operand::VREG_DEF);    \
			opr1.reg = r1;			\
			opr2.reg = r2;			\
			opr3.reg = r3;			\
			opr4.reg = r4;			\
			return new VInst4(OP_##op, OPK_COMPUTATION, opr1, opr2, opr3, opr4);	\
		}
		CREATECOMP4(MUL)
			CREATECOMP4(MULU)
			CREATECOMP4(DIV)
			CREATECOMP4(DIVU)
#undef CREATECOMP4

			// cc: compare condition code
			// r1: v-register number, left operand
			// r2: v-register number, right operand
			// r3: v-register number, where compare result (true/false) is stored
			static VInst * createCMP(CmpCC cc, XTRegNum r1, XTRegNum r2, XTRegNum r3)
			{
				Operand opr1(Operand::CC),    
					opr2(Operand::VREG_USE),    
					opr3(Operand::VREG_USE),
					opr4(Operand::VREG_DEF); 

				opr1.cc = cc;
				opr2.reg = r1;
				opr3.reg = r2;
				opr4.reg = r3;
				return new VInst4(OP_CMP, OPK_COMPUTATION, opr1, opr2, opr3, opr4);
			}

		// cc: compare condition code
		// r1: float-point register number, left operand
		// r2: float-point register number, right operand
		// r3: float-point register number, where compare result (true/false) is stored
		static VInst * createFCMPS(CmpCC cc, XTRegNum r1, XTRegNum r2, XTRegNum r3)
		{
			Operand opr1(Operand::CC),    
				opr2(Operand::FREG),    
				opr3(Operand::FREG),
				opr4(Operand::FREG); 

			opr1.cc = cc;
			opr2.reg = r1;
			opr3.reg = r2;
			opr4.reg = r3;
			return new VInst4(OP_FCMP_S, OPK_COMPUTATION, opr1, opr2, opr3, opr4);
		}

		// cc: compare condition code
		// r1: float-point register number, left operand
		// r2: float-point register number, right operand
		// r3: float-point register number, where compare result (true/false) is stored
		static VInst * createFCMPD(CmpCC cc, XTRegNum r1, XTRegNum r2, XTRegNum r3)
		{
			Operand opr1(Operand::CC),    
				opr2(Operand::FREG),    
				opr3(Operand::FREG),
				opr4(Operand::FREG); 

			opr1.cc = cc;
			opr2.reg = r1;
			opr3.reg = r2;
			opr4.reg = r3;
			return new VInst4(OP_FCMP_D, OPK_COMPUTATION, opr1, opr2, opr3, opr4);
		}

	protected:
		VInst4 (Opcode op, Opkind kind, 
				Operand opr1, Operand opr2, Operand opr3, Operand opr4)
			: VInst(op, kind)
			{
				operand[0] = opr1;
				operand[1] = opr2;
				operand[2] = opr3;
				operand[3] = opr4;
			}

	private:
		Operand operand[4];
};

// VInst with FIVE operand
class VInst5 : public VInst {
	public:
		// Attention: only valide when i == 1, otherwise, undefined.
		Operand getOperand(int i) const
		{
			return operand[i - 1];
		}

		void setOperand(int i, Operand oprnd)
		{
			operand[i - 1] = oprnd;
		}

		int getOperandNum() const { return 5; }

		static VInst * createBRANCH(CmpCC cc, XTRegNum r1, XTRegNum r2, 
				XTRegNum reg, XTInt32 disp)
		{
			Operand opr1(Operand::CC),
			opr2(Operand::VREG_USE),
			opr3(Operand::VREG_USE),
			opr4(Operand::VREG_USE),
			opr5(Operand::IMM);

			opr1.cc = cc;
			opr2.reg = r1;
			opr3.reg = r2;
			opr4.reg = reg;
			opr5.imm = disp;
			return new VInst5(OP_BRANCH, OPK_CONTROLTRANSFER, 
					opr1, opr2, opr3, opr4, opr5);
		}

	protected:
		VInst5(Opcode op, Opkind kind, 
				Operand opr1, Operand opr2, Operand opr3, Operand opr4, Operand opr5) 
			:VInst(op, kind)
			{
				operand[0] = opr1;
				operand[1] = opr2;
				operand[2] = opr3;
				operand[3] = opr4;
				operand[4] = opr5;
			}

	private:
		Operand operand[5];
};


class VInstVar : public VInst {
	public:

		Operand getOperand(int i) const
		{
			return operand[i - 1];
		}

		void setOperand(int i, Operand oprnd)
		{
			operand[i - 1] = oprnd;
		}

		int getOperandNum() const
		{
			return operand.size();
		}

		static VInst * createCALL (XTInt32 disp, int n, ...)
		{
			VInstVar *inst = new VInstVar(OP_CALL, OPK_SPECIAL);		

			Operand func(Operand::WORD), 
				argN(Operand::IMM), 
				arg(Operand::VREG_USE);

			func.word = disp;
			argN.imm = n;

			inst->operand.push_back(func);
			inst->operand.push_back(argN);

			// push all the function arguments
			va_list ap;
			va_start(ap, n);
			for (int i = 0; i < n; i++) {
				arg.reg = va_arg(ap, XTInt32);
				inst->operand.push_back(arg);
			}
			va_end(ap);

			return inst;
		}

	protected:
		VInstVar (Opcode op, Opkind kind)
			: VInst(op, kind)
			{
			}

	private:
		std::vector<Operand> operand;
};

}

#endif
